package model

import (
	"log"
	"github.com/go-gl/mathgl/mgl32"
	"git.oschina.net/zdglf/gommd/util"
	"git.oschina.net/zdglf/gommd/gles2"
	"path"
	"unsafe"
)

type PMXModel struct {

	program int32
	programMap map[string]int32
	pmx *util.PMX
	//vmd *util.VMD
	vbuffers map[string]GLBuf

	textureManager *TextureManager


	normals []float32
	uvs []float32
	positions []float32
	morphVec []float32
	edge []float32


	x int32
	y int32
	width int32
	height int32

	rotx float32
	roty float32
	fovy float32
	distance float32
	center mgl32.Vec3
	cameraPosition mgl32.Vec3
	upPos mgl32.Vec3

	edgeThickness float32
	edgeColor []float32
	lightDirection mgl32.Vec3
	lightColor []float32
	drawEdge bool

	viewMatrix mgl32.Mat4
	pMatrix mgl32.Mat4
	modelMatrix mgl32.Mat4
	mvMatrix mgl32.Mat4
	nMatrix mgl32.Mat4


}

func (m *PMXModel)LoadFile(filePath string, fileName string) bool  {
	m.pmx = new(util.PMX)
	log.Println("start pmd load")
	if err := m.pmx.Load(filePath, fileName); err != nil{
		m.pmx = nil
		log.Println(err)
		return false
	}else {
		log.Println(m.pmx.Name)
		log.Println(m.pmx.Comment)
		return true
	}
}

func (m *PMXModel)InitShader(vShader string, fShader string) bool {
	vertexShader := gles2.CreateShader(gles2.VERTEX_SHADER)
	gles2.ShaderSource(vertexShader,1, []string{vShader}, []int32{int32(len(vShader))})
	gles2.CompileShader(vertexShader)
	var compileStatus int32
	gles2.GetShaderiv(vertexShader, gles2.COMPILE_STATUS, &compileStatus)
	if compileStatus == 0{
		var maxLength int32 = 1024
		infoBytes := make([]byte, maxLength)
		var realLength int32
		gles2.GetShaderInfoLog(vertexShader, maxLength, &realLength, infoBytes)
		log.Println("vshader", string(infoBytes[:realLength]))
		gles2.DeleteShader(vertexShader)
		vertexShader = 0
	}
	if vertexShader == 0{
		return false
	}

	fragmentShader := gles2.CreateShader(gles2.FRAGMENT_SHADER)
	gles2.ShaderSource(fragmentShader, 1, []string{fShader}, []int32{int32(len(fShader))})
	gles2.CompileShader(fragmentShader)
	gles2.GetShaderiv(fragmentShader, gles2.COMPILE_STATUS, &compileStatus)
	if compileStatus == 0{
		var maxLength int32 = 1024
		infoBytes := make([]byte, maxLength)
		var realLength int32
		gles2.GetShaderInfoLog(fragmentShader, maxLength, &realLength, infoBytes)
		log.Println("fshader", string(infoBytes[:realLength]))
		gles2.DeleteShader(fragmentShader)
		fragmentShader = 0
	}
	if fragmentShader == 0{
		return false
	}
	m.program = gles2.CreateProgram()
	gles2.AttachShader(m.program, vertexShader)
	gles2.AttachShader(m.program, fragmentShader)
	gles2.LinkProgram(m.program)
	var linkStatus int32
	gles2.GetProgramiv(m.program, gles2.LINK_STATUS, &linkStatus)
	if linkStatus == 0{
		var maxLength int32 = 1024
		infoBytes := make([]byte, maxLength)
		var realLength int32
		gles2.GetProgramInfoLog(m.program, maxLength, &realLength, infoBytes)
		log.Println("link", string(infoBytes[:realLength]))
		m.program = 0
	}
	if m.program == 0{
		return false
	}
	gles2.UseProgram(m.program)
	m.programMap = make(map[string]int32)

	var count int32
	gles2.GetProgramiv(m.program, gles2.ACTIVE_ATTRIBUTES, &count)

	for i:= 0;i <int(count);i++{
		var bufSize int32 = 20
		var realSize int32
		var attr_type int32
		var nameSize int32
		buff := make([]byte, bufSize)
		gles2.GetActiveAttrib(m.program, int32(i), bufSize, &nameSize, &realSize, &attr_type, buff)
		name := string(buff[:nameSize])
		m.programMap[name] = int32(i)
		gles2.EnableVertexAttribArray(int32(i))
		log.Println(i,name)
	}
	gles2.GetProgramiv(m.program, gles2.ACTIVE_UNIFORMS, &count)
	for i:= 0;i <int(count);i++{
		var bufSize int32 = 20
		var realSize int32
		var attr_type int32
		var nameSize int32
		buff := make([]byte, bufSize)
		gles2.GetActiveUniform(m.program, int32(i), bufSize, &nameSize, &realSize, &attr_type, buff)
		name := string(buff[:nameSize])
		m.programMap[name] = int32(i)
		log.Println(i, name)
	}
	log.Println("InitShader:", gles2.GetError())

	return true
}
func (m *PMXModel)InitParam(x int32, y int32, width int32, height int32, toonDir string){
	m.distance = 15.0
	m.rotx = 0.0
	m.roty = 0.0
	m.fovy = 40.0
	m.cameraPosition = mgl32.Vec3{0.0, 0.0, -15.0}
	m.upPos = mgl32.Vec3{0,1,0.0}
	m.center =  mgl32.Vec3{0.0, 10.0, 0.0}


	m.drawEdge = false
	m.edgeThickness = 0.004
	m.edgeColor = []float32{0.0, 0.0, 0.0, 1.0}
	m.lightDirection = mgl32.Vec3{1.0, -1.0, -1.0}
	m.lightColor = []float32{0.6, 0.6, 0.6, 1.0}


	m.x = x
	m.y = y
	m.width = width
	m.height = height

	m.initVertices()
	if err := m.initTextures(toonDir);err!=nil{
		log.Println(err)
	}
	log.Println("InitParam:", gles2.GetError())

}

func (m *PMXModel)initVertices(){
	m.vbuffers = make(map[string]GLBuf)
	if m.pmx != nil{
		length := len(m.pmx.Vertices)
		m.positions = make([]float32, 3*length)
		m.morphVec = make([]float32, 3*length)
		m.normals = make([]float32, 3*length)
		m.uvs = make([]float32, 2*length)
		m.edge = make([]float32, length)

		for i:= 0; i< length; i++ {
			vertex := m.pmx.Vertices[i]
			m.positions[ 3 * i] = vertex.X
			m.positions[ 3 * i + 1] = vertex.Y
			m.positions[ 3 * i + 2] = vertex.Z
			m.normals[3 * i] = vertex.NX
			m.normals[3 * i + 1] = vertex.NY
			m.normals[3 * i + 2] = vertex.NZ
			m.uvs[2 * i] = vertex.U
			m.uvs[2 * i + 1] = vertex.V

		}
		tmpArr := make([]AttrArrBuf, 0)
		tmpArr = append(tmpArr, AttrArrBuf{3, m.positions, "aPosition"})
		tmpArr = append(tmpArr, AttrArrBuf{3, m.morphVec, "aMultiPurposeVector"})
		tmpArr = append(tmpArr, AttrArrBuf{3, m.normals, "aVertexNormal"})
		tmpArr = append(tmpArr, AttrArrBuf{2, m.uvs, "aTextureCoord"})
		tmpArr = append(tmpArr, AttrArrBuf{1, m.edge, "aVertexEdge"})
		for _, tmp := range tmpArr{
			buffer := make([]int32, 1)
			gles2.GenBuffers(1, buffer)
			gles2.BindBuffer(gles2.ARRAY_BUFFER, buffer[0])
			gles2.BufferData(gles2.ARRAY_BUFFER, 4 * len(tmp.array), unsafe.Pointer(&tmp.array[0]), gles2.STATIC_DRAW)

			m.vbuffers[tmp.attribute] = GLBuf{tmp.size, buffer[0]}

		}
		gles2.BindBuffer(gles2.ARRAY_BUFFER, 0)
		log.Println("initVertices:", gles2.GetError())
	}
}

func (m *PMXModel)initTextures(toonDir string)(err error){
	m.textureManager = NewTextureManager()
	toonFiles := []string{"toon00.bmp", "toon01.bmp", "toon02.bmp", "toon03.bmp", "toon04.bmp", "toon05.bmp",
		"toon06.bmp", "toon07.bmp", "toon08.bmp", "toon09.bmp", "toon10.bmp"}
	materials := m.pmx.Materials
	for _, material := range materials{
		if material.Textures == nil{
			material.Textures = make(map[string]int32)
		}

		log.Println("enviroment mode ", material.EnvironmentBlendMode)
		if(material.EnvironmentIndex == -1){
			material.EnvironmentBlendMode = util.MATERIAL_MODE_DISABLE
		}
		switch material.EnvironmentBlendMode {
		case util.MATERIAL_MODE_SPH:
			log.Println("enviroment ", material.EnvironmentIndex)
			if material.Textures["sph"], err = m.textureManager.Get("sph", path.Join(m.pmx.Directory, m.pmx.Textures[material.EnvironmentIndex]));err != nil{
				return
			}

		case util.MATERIAL_MODE_SPA:
			log.Println("enviroment ", material.EnvironmentIndex)
			if material.Textures["spa"], err = m.textureManager.Get("spa", path.Join(m.pmx.Directory, m.pmx.Textures[material.EnvironmentIndex]));err != nil{
				return
			}

		case util.MATERIAL_MODE_SPS:
			log.Println("enviroment ", material.EnvironmentIndex)
			if material.Textures["sps"], err = m.textureManager.Get("sps", path.Join(m.pmx.Directory, m.pmx.Textures[material.EnvironmentIndex]));err != nil{
				return
			}

		}
		if(material.ToonValue != -1){
			switch material.ToonReference {
			case util.MATERIAL_TEXTURE_REFERENCE:
				log.Println("toon", material.ToonValue)
				if material.Textures["toon"], err = m.textureManager.Get("toon", path.Join(m.pmx.Directory, m.pmx.Textures[material.ToonValue]));err != nil{
					return
				}
			case util.MATERIAL_INTERNAL_REFERENCE:
				log.Println("toon", material.ToonValue)
				if material.Textures["toon"], err = m.textureManager.Get("toon", path.Join(toonDir, toonFiles[material.ToonValue]));err != nil{
					return
				}
			}
		}
		if material.TextureIndex != -1{
			if material.Textures["regular"], err = m.textureManager.Get("regular", path.Join(m.pmx.Directory, m.pmx.Textures[material.TextureIndex]));err != nil{
				return
			}
		}

	}

	return
}

//func (m *PMXModel)LoadMotion(filePath string) bool{
//	m.vmd = new(util.VMD)
//	if err := m.vmd.Load(filePath); err != nil{
//		m.vmd = nil
//		return false
//	}
//	return true
//}
//func (m *PMXModel)GetFrameCount() int{
//	if m.vmd != nil{
//		return m.vmd.MaxFrame
//	}
//	return 0
//}
//func (m *PMXModel)InitFrame(index int){
//	if m.vmd != nil&&index<m.vmd.MaxFrame{
//		m.moveBones(index)
//		m.moveCamera(index)
//	}
//
//}
//
//func (m *PMXModel)moveBones(index int){
//	if m.vmd != nil {
//		bones := m.pmx.Bones
//		bones_count := len(bones)
//		bone_frames := m.vmd.GetBoneFrame(index)
//
//
//		individualBoneMotions := make([]*util.VMDBoneKeyFrame, bones_count)
//		originalBonePositions := make([][]float32, bones_count)
//		parentBones := make([]int, bones_count)
//		constrainedBones := make([]bool, bones_count)
//
//		for i := 0;i < bones_count; i++{
//			bone := bones[i]
//			//骨头的位置
//			originalBonePositions[i] = bone.Pos
//			//包含 ひざ "关节" 的日语
//			if strings.Contains(bone.Name, "ひざ"){
//				constrainedBones[i] = true
//			}else{
//				constrainedBones[i] = false
//			}
//			//父骨头的index
//			parentBones[i] = bone.ParentBoneIndex
//			//判断是否有骨头有同名的动作
//			individualBoneMotions[i] = nil
//			for j:= 0;j<bone_frames.Len();j++{
//				bone_frame := bone_frames[j];
//				if strings.Compare(bone_frame.Name, bone.Name) == 0{
//					individualBoneMotions[i] = bone_frame
//					break
//				}
//			}
//			//初始化没有同名的动作骨头
//			if individualBoneMotions[i] == nil{
//				individualBoneMotions[i] = new(util.VMDBoneKeyFrame)
//				individualBoneMotions[i].Location = []float32{0, 0, 0}
//				individualBoneMotions[i].Rotation = []float32{0, 0, 0, 1}
//
//			}
//
//		}
//
//		m.vbuffers = make(map[string]GLBuf)
//		if m.pmx != nil{
//			length := len(m.pmx.Vertices)
//			for i:= 0; i< length; i++ {
//				vertex := m.pmx.Vertices[i]
//				normal := mgl32.Vec3{vertex.NX, vertex.NY, vertex.NZ}
//				position := mgl32.Vec3{vertex.X, vertex.Y, vertex.Z}
//				switch vertex.WeightType {
//				case util.VERTEX_BDEF1:
//					position = m.calculatePosition(position, vertex.BoneIndex1, originalBonePositions, parentBones, individualBoneMotions)
//					normal = m.calculateNormal(normal, vertex.BoneIndex1, parentBones, individualBoneMotions)
//				case util.VERTEX_BDEF2, util.VERTEX_SDEF:
//					p1 := m.calculatePosition(position, vertex.BoneIndex1, originalBonePositions, parentBones, individualBoneMotions)
//					n1 := m.calculateNormal(normal, vertex.BoneIndex1, parentBones, individualBoneMotions)
//
//					p2 := m.calculatePosition(position, vertex.BoneIndex2, originalBonePositions, parentBones, individualBoneMotions)
//					n2 := m.calculateNormal(normal, vertex.BoneIndex2, parentBones, individualBoneMotions)
//					normal = n1.Mul(vertex.Bone1Weight).Add(n2.Mul(vertex.Bone2Weight)).Normalize()
//					position = p1.Mul(vertex.Bone1Weight).Add(p2.Mul(vertex.Bone2Weight))
//
//				case util.VERTEX_BDEF4, util.VERTEX_QDEF:
//					p1 := m.calculatePosition(position, vertex.BoneIndex1, originalBonePositions, parentBones, individualBoneMotions)
//					n1 := m.calculateNormal(normal, vertex.BoneIndex1, parentBones, individualBoneMotions)
//
//					p2 := m.calculatePosition(position, vertex.BoneIndex2, originalBonePositions, parentBones, individualBoneMotions)
//					n2 := m.calculateNormal(normal, vertex.BoneIndex2, parentBones, individualBoneMotions)
//
//					p3 := m.calculatePosition(position, vertex.BoneIndex3, originalBonePositions, parentBones, individualBoneMotions)
//					n3 := m.calculateNormal(normal, vertex.BoneIndex3, parentBones, individualBoneMotions)
//
//					p4 := m.calculatePosition(position, vertex.BoneIndex4, originalBonePositions, parentBones, individualBoneMotions)
//					n4 := m.calculateNormal(normal, vertex.BoneIndex4, parentBones, individualBoneMotions)
//					normal = (n1.Mul(vertex.Bone1Weight).Add(n2.Mul(vertex.Bone2Weight).Add(n3.Mul(vertex.Bone3Weight).Add(n4.Mul(vertex.Bone4Weight))))).Mul(1.0/(vertex.Bone1Weight+vertex.Bone2Weight+vertex.Bone3Weight+vertex.Bone4Weight)).Normalize()
//					position = (p1.Mul(vertex.Bone1Weight).Add(p2.Mul(vertex.Bone2Weight).Add(p3.Mul(vertex.Bone3Weight).Add(p4.Mul(vertex.Bone4Weight))))).Mul(1.0/(vertex.Bone1Weight+vertex.Bone2Weight+vertex.Bone3Weight+vertex.Bone4Weight))
//
//				}
//				m.positions[ 3 * i] = position.X()
//				m.positions[ 3 * i + 1] = position.Y()
//				m.positions[ 3 * i + 2] = position.Z()
//				m.normals[3 * i] = normal.X()
//				m.normals[3 * i + 1] = normal.Y()
//				m.normals[3 * i + 2] = normal.Z()
//
//			}
//			tmpArr := make([]AttrArrBuf, 0)
//			tmpArr = append(tmpArr, AttrArrBuf{3, m.positions, "aPosition"})
//			tmpArr = append(tmpArr, AttrArrBuf{3, m.morphVec, "aMultiPurposeVector"})
//			tmpArr = append(tmpArr, AttrArrBuf{3, m.normals, "aVertexNormal"})
//			tmpArr = append(tmpArr, AttrArrBuf{2, m.uvs, "aTextureCoord"})
//			tmpArr = append(tmpArr, AttrArrBuf{1, m.edge, "aVertexEdge"})
//			for _, tmp := range tmpArr{
//				buf := m.vbuffers[tmp.attribute]
//				gles2.DeleteBuffers(1, []int32{buf.buffer})
//				buffer := make([]int32, 1)
//				gles2.GenBuffers(1, buffer)
//				gles2.BindBuffer(gles2.ARRAY_BUFFER, buffer[0])
//				gles2.BufferData(gles2.ARRAY_BUFFER, 4 * len(tmp.array), unsafe.Pointer(&tmp.array[0]), gles2.STATIC_DRAW)
//
//				m.vbuffers[tmp.attribute] = GLBuf{tmp.size, buffer[0]}
//
//			}
//			gles2.BindBuffer(gles2.ARRAY_BUFFER, 0)
//			log.Println("initVertices:", gles2.GetError())
//		}
//
//	}
//
//}
//
//func (m *PMXModel)calculatePosition(v mgl32.Vec3, index int,origin_pos [][]float32, parentIndexs []int, frames[]*util.VMDBoneKeyFrame)(ret mgl32.Vec3){
//	rotation_stack := make([]mgl32.Quat, 0)
//	location_stack := make([]mgl32.Vec3, 0)
//	position_stack := make([]mgl32.Vec3, 0)
//	current_index := index
//	count := 0
//	for current_index!=-1{
//		frame := frames[current_index]
//		rot_data := frame.Rotation
//		loc_data := frame.Location
//		pos_data := origin_pos[current_index]
//		position_stack = append(position_stack, mgl32.Vec3{pos_data[0], pos_data[1], pos_data[2]})
//		location_stack = append(location_stack, mgl32.Vec3{loc_data[0], loc_data[1], loc_data[2]})
//		rotation_stack = append(rotation_stack, mgl32.Quat{W:rot_data[3], V:mgl32.Vec3{rot_data[0], rot_data[1], rot_data[2]}})
//		current_index = parentIndexs[current_index]
//		count +=1
//	}
//	ret = v
//	for i := count -1; i>=0;i--{
//		position := position_stack[i]
//		location := location_stack[i]
//		rotation := rotation_stack[i]
//		ret = ret.Sub(position)
//
//		ret =mgl32.Mat4{
//			rotation.X(),0,0,position.X()+location.X(),
//			0,rotation.Y(),0,position.Y()+location.Y(),
//			0,0,rotation.Z(),position.Z()+location.Z(),
//			0,0,0,rotation.W,
//		}.Mul4x1(mgl32.Vec4{ret.X(),ret.Y(),ret.Z(),1.0}).Vec3();
//
//	}
//	return
//
//}
//
//func (m *PMXModel)calculateNormal(n mgl32.Vec3, index int, parentIndexs []int, frames[]*util.VMDBoneKeyFrame)(ret mgl32.Vec3){
//	rotation_stack := make([]mgl32.Quat, 0)
//	current_index := index
//	count := 0
//	for current_index!=-1{
//		frame := frames[current_index]
//		rot_data := frame.Rotation
//		rotation_stack = append(rotation_stack, mgl32.Quat{W:rot_data[3], V:mgl32.Vec3{rot_data[0], rot_data[1], rot_data[2]}})
//		current_index = parentIndexs[current_index]
//		count +=1
//	}
//	ret = n
//	for i := count -1; i>=0;i--{
//		rotation := rotation_stack[i]
//		ret = rotation.Rotate(ret).Normalize()
//	}
//	return
//}
//func (m *PMXModel)moveCamera(index int){
//	if m.vmd != nil {
//		camera := m.vmd.GetCameraFrame(index)
//		if camera != nil {
//			log.Println(index, camera.Distance, camera.Location)
//			m.distance = camera.Distance
//			m.center = mgl32.Vec3{camera.Location[0], camera.Location[1], camera.Location[2]}
//			m.fovy = float32(camera.ViewAngle)
//			m.rotx = camera.Rotation[0]
//			m.roty = camera.Rotation[1]
//
//		}else{
//			log.Println("camera not exit");
//		}
//	}
//
//}

func (m *PMXModel)Render (){

	m.computeMatrices()
	gles2.ClearColor(0.5, 0.5, 0.5, 1)
	gles2.ClearDepthf(1)
	gles2.Enable(gles2.DEPTH_TEST)

	gles2.BindFramebuffer(gles2.FRAMEBUFFER, 0)
	gles2.Viewport(m.x, m.y, m.width, m.height)
	gles2.Clear(gles2.COLOR_BUFFER_BIT | gles2.DEPTH_BUFFER_BIT)
	for attr, vb := range m.vbuffers{
		gles2.BindBuffer(gles2.ARRAY_BUFFER, vb.buffer)
		gles2.VertexAttribPointer(m.programMap[attr], int32(vb.size), gles2.FLOAT, byte(0), 0, nil)
	}

	m.setUniforms()
	gles2.Enable(gles2.CULL_FACE)
	gles2.Enable(gles2.BLEND)
	gles2.BlendFuncSeparate(gles2.SRC_ALPHA, gles2.ONE_MINUS_SRC_ALPHA, gles2.SRC_ALPHA, gles2.DST_ALPHA)
	offset := 0
	materials := m.pmx.Materials

	for _,material := range materials{
		m.renderMaterial(material, offset)

		offset += material.SurfaceCount
	}

	gles2.Disable(gles2.BLEND)
	offset = 0
	for _,material := range materials{
		m.renderEdge(material, offset)
		offset += material.SurfaceCount
	}

	gles2.Disable(gles2.CULL_FACE)
	gles2.Flush()

}

func (m *PMXModel)setUniforms()  {
	m.modelMatrix = mgl32.Ident4()
	m.mvMatrix = m.viewMatrix.Mul4(m.modelMatrix)
	m.nMatrix = m.mvMatrix.Inv()
	m.nMatrix = m.nMatrix.Transpose()

	gles2.Uniform1f(m.programMap["uEdgeThickness"], m.edgeThickness)
	gles2.Uniform3fv(m.programMap["uEdgeColor"], 1, &m.edgeColor[0])
	gles2.UniformMatrix4fv(m.programMap["uMVMatrix"], 1, byte(0), &m.mvMatrix[0])
	gles2.UniformMatrix4fv(m.programMap["uPMatrix"], 1, byte(0), &m.pMatrix[0])
	gles2.UniformMatrix4fv(m.programMap["uNMatrix"], 1, byte(0), &m.nMatrix[0])

	ld := m.lightDirection.Normalize()
	ld4 := m.nMatrix.Mul4x1(ld.Vec4(0))
	ld = ld4.Vec3()
	gles2.Uniform3fv(m.programMap["uLightDirection"], 1, &ld[0])
	gles2.Uniform3fv(m.programMap["uLightColor"], 1, &m.lightColor[0])

	gles2.Uniform1i(m.programMap["uSelfShadow"], 0)

	gles2.Uniform1i(m.programMap["uGenerateShadowMap"], 0)
	gles2.Uniform1i(m.programMap["uAxis"], 0)
	gles2.Uniform1i(m.programMap["uCenterPoint"], 0)
	log.Println("setUniforms:", gles2.GetError())


}

func (m *PMXModel)renderMaterial(material *util.PMXMaterial, offset int)  {
	gles2.Uniform3fv(m.programMap["uAmbientColor"],1, &material.AmbientColor[0])
	gles2.Uniform3fv(m.programMap["uSpecularColor"],1, &material.SpecularColor[0])
	gles2.Uniform3fv(m.programMap["uDiffuseColor"], 1, &material.DiffuseColor[0])
	gles2.Uniform1f(m.programMap["uAlpha"], material.Alpha)
	gles2.Uniform1f(m.programMap["uShininess"], material.SpecularStrength)
	gles2.Uniform1i(m.programMap["uEdge"], 0)
	textures := material.Textures
	if _,ok := textures["toon"];ok{
		gles2.ActiveTexture(gles2.TEXTURE0)
		gles2.BindTexture(gles2.TEXTURE_2D, textures["toon"])
		gles2.Uniform1i(m.programMap["uToon"], 0)
		gles2.Uniform1i(m.programMap["uUseToon"], 1)
	}else{
		gles2.Uniform1i(m.programMap["uUseToon"], 0)
	}
	if _,ok := textures["regular"];ok{
		gles2.ActiveTexture(gles2.TEXTURE1)
		gles2.BindTexture(gles2.TEXTURE_2D, textures["regular"])
		gles2.Uniform1i(m.programMap["uTexture"], 1)
		gles2.Uniform1i(m.programMap["uUseTexture"], 1)

	}else{
		gles2.Uniform1i(m.programMap["uUseTexture"], 0)
	}
	gles2.Uniform1i(m.programMap["uSphereMapMode"], int32(material.EnvironmentBlendMode))
	switch material.EnvironmentBlendMode {
	case util.MATERIAL_MODE_SPH:
		gles2.ActiveTexture(gles2.TEXTURE2)
		gles2.BindTexture(gles2.TEXTURE_2D, textures["sph"])
		gles2.Uniform1i(m.programMap["uSphereMap"], 2)
	case util.MATERIAL_MODE_SPA:
		gles2.ActiveTexture(gles2.TEXTURE2)
		gles2.BindTexture(gles2.TEXTURE_2D, textures["spa"])
		gles2.Uniform1i(m.programMap["uSphereMap"], 2)
	case util.MATERIAL_MODE_SPS:
		gles2.ActiveTexture(gles2.TEXTURE2)
		gles2.BindTexture(gles2.TEXTURE_2D, textures["sps"])
		gles2.Uniform1i(m.programMap["uSphereMap"], 2)

	}

	gles2.CullFace(gles2.FRONT)
	gles2.DrawElements(gles2.TRIANGLES, int32(material.SurfaceCount), gles2.UNSIGNED_INT, unsafe.Pointer(&m.pmx.Triangles[offset]))

}

func (m *PMXModel)renderEdge(material *util.PMXMaterial, offset int)  {
	if (!m.drawEdge) {
		return
	}
	gles2.Uniform1i(m.programMap["uEdge"], 1)
	gles2.CullFace(gles2.BACK)
	gles2.DrawElements(gles2.TRIANGLES, int32(material.SurfaceCount), gles2.UNSIGNED_INT, unsafe.Pointer(&m.pmx.Triangles[offset]))
	gles2.CullFace(gles2.FRONT);
	gles2.Uniform1i(m.programMap["uEdge"], 0)

}

func (m *PMXModel)computeMatrices(){
	m.cameraPosition = mgl32.Vec3{0.0, 0.0,-m.distance}
	m.upPos = mgl32.Vec3{0, 1,0.0}

	rotx_mat3 := mgl32.Rotate3DX(m.rotx)
	m.cameraPosition = rotx_mat3.Mul3x1(m.cameraPosition)
	roty_mat3 := mgl32.Rotate3DX(m.roty)
	m.cameraPosition = roty_mat3.Mul3x1(m.cameraPosition)

	m.cameraPosition = m.cameraPosition.Add(m.center)

	gles2.Enable(gles2.CULL_FACE)
	gles2.Enable(gles2.DEPTH_TEST)
	gles2.Viewport(m.x,m.y,m.width,m.height)

	ratio := float32(m.width) / float32(m.height)
	near := float32(0.1)
	far := float32(100)

	rotx_mat3 = mgl32.Rotate3DX(m.rotx)
	m.upPos = rotx_mat3.Mul3x1(m.upPos)
	roty_mat3 = mgl32.Rotate3DX(m.roty)
	m.upPos = roty_mat3.Mul3x1(m.upPos)

	m.viewMatrix = mgl32.LookAtV(m.cameraPosition, m.center, m.upPos)
	m.pMatrix = mgl32.Perspective(m.fovy, ratio, near, far)

}
